"""
Case Type   : mot
Case Name   : 打开enable_mot_server参数，使用gs_probackup增量备份、恢复，前后数据一致
Create At   : 2025/4/7
Owner       : @li-xin12345
Description : (编译带mot，不编译ENABLE_X86_RDTSCP，对应转测包)
    1、设置enable_incremental_checkpoint=off，enable_mot_server=on,
    enable_cbm_tracking=on
    2、建表
    3、使用gs_probackup全量备份
    4、插入数据
    5、查看表占用磁盘大小、数据量等
    6、使用gs_probackup增量备份
    7、删除表，停止数据库
    8、恢复
    9、查看表占用磁盘大小、数据量等，与备份前对比
    10、清理环境
Expect      :
    1、成功
    2、成功
    3、成功
    4、成功
    5、成功
    6、成功
    7、成功
    8、成功
    9、一致
    10、成功
History     :
"""

import os
import time
import unittest
import re

from yat.test import Node
from yat.test import macro

from testcase.utils.Common import Common
from testcase.utils.CommonSH import CommonSH
from testcase.utils.Constant import Constant
from testcase.utils.Logger import Logger

primary_db_sh = CommonSH('PrimaryDbUser')
cluster_node_count = primary_db_sh.get_node_num()


@unittest.skipIf(3 > cluster_node_count, '不满足一主两备环境跳过')
class MotTest(unittest.TestCase):
    def setUp(self):
        self.log = Logger()
        self.log.info(f'-----{os.path.basename(__file__)} start-----')
        self.std1_db_sh = CommonSH('Standby1DbUser')
        self.std2_db_sh = CommonSH('Standby2DbUser')
        self.pri_node_sh = Node('PrimaryDbUser')
        self.constant = Constant()
        self.common = Common()
        self.tb_name = 't_guc_mot_0025'
        self.view_name = 'v_guc_mot_0025'
        self.backup_dir = 'f_guc_mot_0025'
        self.param_names = ['enable_incremental_checkpoint',
                            'enable_mot_server', 'enable_cbm_tracking']
        self.default_values = [self.common.show_param(i) for i in
                               self.param_names]
        self.log.info(self.default_values)
        self.backup_path = os.path.join(os.path.dirname(
            macro.DB_INSTANCE_PATH), self.backup_dir)

    def test_mot(self):
        text = '-----step1:设置enable_incremental_checkpoint=off，' \
               'enable_mot_server=on,enable_cbm_tracking=on;expect:成功-----'
        self.log.info(text)
        res = primary_db_sh.execute_gsguc('set',
                                        self.constant.GSGUC_SUCCESS_MSG,
                                        f'{self.param_names[0]}=off')
        self.log.info(res)
        self.assertTrue(res, "执行gs_guc set失败，预期成功但未找到标志信息")
        res = primary_db_sh.execute_gsguc('set',
                                        self.constant.GSGUC_SUCCESS_MSG,
                                        f'{self.param_names[1]}=on')
        self.log.info(res)
        self.assertTrue(res, "执行gs_guc set失败，预期成功但未找到标志信息")
        res = primary_db_sh.execute_gsguc('set',
                                          self.constant.GSGUC_SUCCESS_MSG,
                                          f'{self.param_names[2]}=on')
        self.log.info(res)
        self.assertTrue(res, "执行gs_guc set失败，预期成功但未找到标志信息")
        res = primary_db_sh.restart_db_cluster()
        self.log.info(res)
        self.assertTrue(res, '执行失败：' + text)
        status = primary_db_sh.get_db_cluster_status()
        self.assertTrue("Normal" or "Degrade" in status)
        res = [self.common.show_param(i) for i in self.param_names]
        self.log.info(res)
        self.assertEqual(['off', 'on', 'on'], res, '执行失败：' + text)

        text = '-----step2:建表;expect:成功-----'
        self.log.info(text)
        sql = f'''drop foreign table if exists {self.tb_name} cascade;
        create foreign table {self.tb_name} (id int primary key, 
        name varchar(100) not null, age int not null default 0);
        create index idx_name on {self.tb_name} (name);
        insert into {self.tb_name} (id, name, age) values (1, 'alice', 25), 
        (2, 'bob', 30),(3, 'charlie', 35);
        select * from {self.tb_name} where age > 30;
        update {self.tb_name} set age = 26 where id = 1;
        begin;
        insert into {self.tb_name} (id, name, age) values (4, 'david', 40);
        commit;
        create or replace view {self.view_name} as
        select id, name, age from {self.tb_name} where age > 30;
        select * from {self.view_name};
        '''
        sql_res = primary_db_sh.execut_db_sql(sql)
        self.log.info(sql_res)
        self.assertIn(self.constant.DROP_FOREIGN_SUCCESS_MSG, sql_res,
                      '执行失败：' + text)
        self.assertIn(self.constant.CREATE_FOREIGN_SUCCESS_MSG, sql_res,
                      '执行失败：' + text)
        self.assertIn(self.constant.CREATE_INDEX_SUCCESS_MSG, sql_res,
                      '执行失败：' + text)
        self.assertIn(self.constant.INSERT_SUCCESS_MSG, sql_res,
                      '执行失败：' + text)
        self.assertIn(self.constant.COMMIT_SUCCESS_MSG, sql_res,
                      '执行失败：' + text)
        self.assertIn(self.constant.CREATE_VIEW_SUCCESS_MSG, sql_res,
                      '执行失败：' + text)
        self.assertIn('2 rows', sql_res, '执行失败：' + text)

        text = '-----step3:使用gs_probackup全量备份;expect:成功-----'
        self.log.info(text)
        text = '--新建目录，进行初始化--'
        self.log.info(text)
        cmd = f"rm -rf {self.backup_path};mkdir -p {self.backup_path};" \
              f"chmod 700 {self.backup_path};" \
              f"ls {os.path.dirname(macro.DB_INSTANCE_PATH)} | " \
              f"grep '{self.backup_dir}';"
        res = self.common.get_sh_result(self.pri_node_sh, cmd)
        self.assertIn(f'{self.backup_dir}', res, '执行失败：' + text)

        cmd = f'source {macro.DB_ENV_PATH};' \
              f'gs_probackup init -B {self.backup_path}'
        res = self.common.get_sh_result(self.pri_node_sh, cmd)
        self.assertIn('successfully inited', res, '执行失败：' + text)

        self.log.info('--在备份路径内初始化一个新的备份实例--')
        cmd = f"source {macro.DB_ENV_PATH};" \
              f"gs_probackup add-instance -B {self.backup_path} " \
              f"-D {macro.DB_INSTANCE_PATH} --instance=pro1"
        res = self.common.get_sh_result(self.pri_node_sh, cmd)
        self.assertIn('successfully inited', res, '执行失败：' + text)

        self.log.info('--进行全量备份--')
        cmd = f"source {macro.DB_ENV_PATH};" \
              f"gs_probackup backup -B {self.backup_path} -b full --stream " \
              f"--instance=pro1 --skip-block-validation " \
              f"--no-validate -p {self.pri_node_sh.db_port} " \
              f"-d {self.pri_node_sh.db_name}"
        res = self.common.get_sh_result(self.pri_node_sh, cmd)
        re.search('Backup * completed', res)

        self.log.info('--查看备份情况--')
        cmd = f"source {macro.DB_ENV_PATH};" \
              f"gs_probackup show -B {self.backup_path} --instance=pro1"
        res = self.common.get_sh_result(self.pri_node_sh, cmd)
        self.assertIn('DONE', res, '执行失败：' + text)

        text = '-----step4:插入数据;expect:成功-----'
        self.log.info(text)
        sql = (f"insert into {self.tb_name} (id, name, age) values "
               f"(generate_series(1000, 100000), 'david', 50);")
        sql_res = primary_db_sh.execut_db_sql(sql)
        self.log.info(sql_res)
        self.assertIn('INSERT 0 99001', sql_res, '执行失败：' + text)

        text = '-----step5:查看表占用磁盘大小、数据量等-----'
        self.log.info(text)
        sql = f"select pg_size_pretty(pg_total_relation_size(" \
              f"'{self.tb_name}')) as table_size;"
        res = primary_db_sh.execut_db_sql(sql)
        self.log.info(res)
        self.assertNotIn('ERROR', res, '执行失败：' + text)

        sql = f'select checksum({self.tb_name}::text) from {self.tb_name};'
        before_size = primary_db_sh.execut_db_sql(sql)
        self.log.info(before_size)
        self.assertNotIn('ERROR', before_size, '执行失败：' + text)

        text = '-----step6:使用gs_probackup增量备份;expect:成功-----'
        self.log.info(text)
        cmd = f"source {macro.DB_ENV_PATH};" \
              f"gs_probackup backup -B {self.backup_path} -b ptrack " \
              f"--instance=pro1 --skip-block-validation " \
              f"--no-validate -p {self.pri_node_sh.db_port} " \
              f"-d {self.pri_node_sh.db_name}"
        res = self.common.get_sh_result(self.pri_node_sh, cmd)
        re.search('Backup * completed', res)

        self.log.info('-----step7:删除表，停止数据库;expect:成功-----')
        sql = f"drop foreign table if exists {self.tb_name} cascade;checkpoint;"
        self.log.info(sql)
        res = primary_db_sh.execut_db_sql(sql)
        self.log.info(res)
        self.assertIn(self.constant.DROP_FOREIGN_SUCCESS_MSG, res,
                      '执行失败：' + text)

        self.log.info('--等待集群数据同步--')
        flag = primary_db_sh.check_data_consistency()
        self.assertTrue(flag, '执行失败：' + text)

        stop_res = primary_db_sh.stop_db_instance()
        self.assertIn(self.constant.GS_CTL_STOP_SUCCESS_MSG, stop_res,
                      '执行失败：' + text)

        text = '-----step8:恢复;expect:成功-----'
        self.log.info(text)
        cmd = f"source {macro.DB_ENV_PATH};" \
              f"gs_probackup restore -B {self.backup_path} --instance=pro1 " \
              f"--incremental-mode=checksum -D {macro.DB_INSTANCE_PATH};"
        res = self.common.get_sh_result(self.pri_node_sh, cmd)
        re.search('Restore of backup * completed', res)

        start_res = primary_db_sh.start_db_instance()
        self.assertIn('server started', start_res, '启动失败')

        status = primary_db_sh.get_db_cluster_status(param='status')
        self.log.info(status)
        if status is False:
            build_res1 = self.std1_db_sh.build_standby(mode='')
            self.assertIn(self.constant.BUILD_SUCCESS_MSG, build_res1)
            build_res2 = self.std2_db_sh.build_standby(mode='')
            self.assertIn(self.constant.BUILD_SUCCESS_MSG, build_res2)
            status = primary_db_sh.get_db_cluster_status(param='status')
            self.log.info(status)
        time.sleep(5)

        text = ('-----step9:再次查看表占用磁盘大小、数据量等，与备份前对比;'
                'expect:一致-----')
        self.log.info(text)
        sql = f"select pg_size_pretty(pg_total_relation_size(" \
              f"'{self.tb_name}')) as table_size;"
        res = primary_db_sh.execut_db_sql(sql)
        self.log.info(res)
        self.assertNotIn('ERROR', res, '执行失败：' + text)

        sql = f'select checksum({self.tb_name}::text) from {self.tb_name};'
        after_size = primary_db_sh.execut_db_sql(sql)
        self.log.info(after_size)
        self.assertNotIn('ERROR', before_size, '执行失败：' + text)
        self.assertEqual(before_size, after_size, '执行失败：' + text)

    def tearDown(self):
        text = '-----step10:清理环境; expect:成功-----'
        self.log.info(text)
        cmd = f"source {macro.DB_ENV_PATH};" \
              f"gs_probackup del-instance -B {self.backup_path} " \
              f"-D {macro.DB_INSTANCE_PATH} --instance=pro1"
        del_res = self.common.get_sh_result(self.pri_node_sh, cmd)
        self.log.info(del_res)

        cmd = f'rm -rf {self.backup_path};' \
              f'if [ -d {self.backup_path} ]; ' \
              f'then echo "exists"; else echo "not exists"; fi'
        rm_res = self.common.get_sh_result(self.pri_node_sh, cmd)

        sql = f'drop foreign table if exists {self.tb_name} cascade;checkpoint;'
        drop_res = primary_db_sh.execut_db_sql(sql)
        self.log.info(drop_res)
        time.sleep(10)

        res = [self.common.show_param(i) for i in self.param_names]
        self.log.info(res)
        if res != self.default_values:
            for i, param_name in enumerate(self.param_names):
                res = primary_db_sh.execute_gsguc(
                    'set', self.constant.GSGUC_SUCCESS_MSG,
                    f'{param_name}={self.default_values[i]}')
                self.log.info(res)
            restart_res = primary_db_sh.restart_db_cluster()
            self.log.info(restart_res)
            status = primary_db_sh.get_db_cluster_status()
            self.log.info(status)
            show_param = [self.common.show_param(i) for i in self.param_names]
            self.log.info(show_param)
            self.assertEqual(self.default_values, show_param,
                             '执行失败：' + text)
            self.assertTrue(restart_res, '执行失败：' + text)

        status = primary_db_sh.get_db_cluster_status(param='status')
        self.log.info(status)
        if status is False:
            build_res1 = self.std1_db_sh.build_standby(mode='')
            self.assertIn(self.constant.BUILD_SUCCESS_MSG, build_res1)
            build_res2 = self.std2_db_sh.build_standby(mode='')
            self.assertIn(self.constant.BUILD_SUCCESS_MSG, build_res2)
            status = primary_db_sh.get_db_cluster_status(param='status')
            self.log.info(status)
        self.assertTrue(status)

        self.assertIn('successfully deleted', del_res, '备份实例删除失败')
        self.assertEqual('not exists', rm_res, '文件删除失败')
        self.assertIn(self.constant.DROP_FOREIGN_SUCCESS_MSG, drop_res,
                      '执行失败：' + text)
        self.assertIn(self.constant.CHECKPOINT_SUCCESS_MSG, drop_res,
                      '执行失败：' + text)
        self.log.info(f'-----{os.path.basename(__file__)} end-----')